home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume18 / spin / part02 < prev    next >
Encoding:
Internet Message Format  |  1989-03-08  |  15.1 KB

  1. Subject:  v18i010:  Simple programmable interface kit, Part01/02
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Jim McBeath <voder!sci!gumby!jimmc>
  7. Posting-number: Volume 18, Issue 10
  8. Archive-name: spin/part01
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 2 (of 2)."
  17. # Contents:  exec.c
  18. # Wrapped by rsalz@fig.bbn.com on Thu Mar  9 15:55:26 1989
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'exec.c' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'exec.c'\"
  22. else
  23. echo shar: Extracting \"'exec.c'\" \(13333 characters\)
  24. sed "s/^X//" >'exec.c' <<'END_OF_FILE'
  25. X/* exec.c - execution for spin
  26. X *
  27. X * 16.Oct.87  jimmc  Initial definition
  28. X * 21.Oct.87  jimmc  Add xexec stuff
  29. X * 22.Oct.87  jimmc  Add I and S arg types
  30. X *  4.Nov.87  jimmc  Add longjmp stuff, use SPescape
  31. X *  5.Nov.87  jimmc  Add SPbool
  32. X * 30.Nov.87  jimmc  Lint cleanup
  33. X * 18.Jan.88  jimmc  Allow negative default values for I arg format
  34. X */
  35. X/* LINTLIBRARY */
  36. X
  37. X#include <stdio.h>
  38. X#include <ctype.h>
  39. X#include <strings.h>
  40. X#include "goto.h"
  41. X#include "xalloc.h"
  42. X#include "spin.h"
  43. X#include "spinparse.h"
  44. X#include "exec.h"
  45. X
  46. Xtypedef char *string;
  47. Xtypedef int (*intfuncp)();
  48. Xtypedef double (*dblfuncp)();
  49. Xtypedef string (*strfuncp)();
  50. Xtypedef SPtoken * (*listfuncp)();
  51. X
  52. Xint (*SPxexecp)();
  53. X
  54. XSPsetxexecp(funcp)
  55. Xint (*funcp)();
  56. X{
  57. X    SPxexecp = funcp;
  58. X}
  59. X
  60. XSPtoken *
  61. XSPnewnil()
  62. X{
  63. XSPtoken *rval;
  64. X
  65. X    ALLOCTOKEN(rval)
  66. X    rval->type = SPTokNil;
  67. X    return rval;
  68. X}
  69. X
  70. XSPtoken *
  71. XSPcopytoken(tk)
  72. XSPtoken *tk;
  73. X{
  74. XSPtoken *newtk, *ltk, *newltk, *prevltk;
  75. X
  76. X    if (!tk) return NIL;
  77. X    if (tk->type==SPTokList) {
  78. X        ALLOCTOKEN(newtk)
  79. X        *newtk = *tk;
  80. X        newtk->value.l = NIL;
  81. X        prevltk = NIL;
  82. X        for (ltk=tk->value.l; ltk; ltk=ltk->next) {
  83. X            newltk = SPcopytoken(ltk);
  84. X            newltk->next = NIL;
  85. X            if (!newtk->value.l) newtk->value.l = newltk;
  86. X            else prevltk->next = newltk;
  87. X            prevltk = newltk;
  88. X        }
  89. X        return newtk;
  90. X    }
  91. X    ALLOCTOKEN(newtk)
  92. X    *newtk = *tk;    /* structure copy */
  93. X    if (tk->type==SPTokStr || tk->type==SPTokName) {
  94. X        newtk->value.s = XALLOCM(char,strlen(tk->value.s)+1,
  95. X                    "copy token");
  96. X        strcpy(newtk->value.s,tk->value.s);
  97. X    }
  98. X    return newtk;
  99. X}
  100. X
  101. XSPtoken *
  102. XSPexec(tk)
  103. XSPtoken *tk;
  104. X{
  105. XSPtoken *SPexeclist(), *SPexecname();
  106. X
  107. X#if 0    /* sometimes useful for debugging */
  108. Xprintf("exec\n");
  109. XSPdumptoken(tk);
  110. X#endif
  111. X    if (!tk) return NIL;
  112. X    if (tk->type!=SPTokList) {    /* treat as constant */
  113. X        return SPcopytoken(tk);
  114. X    }
  115. X/* It is a list, so we need to examine the first item in the list
  116. X * and base our mode of execution on that item.
  117. X */
  118. X    tk = tk->value.l;
  119. X    if (!tk) return SPnewnil();
  120. X    switch (tk->type) {
  121. X    case SPTokList:
  122. X        return SPexeclist(tk);
  123. X    case SPTokName:
  124. X        return SPexecname(tk);
  125. X    default:
  126. X        SPescape("BadExecList",
  127. X            "bad node type %c in list execution",tk->type);
  128. X        /* NOTREACHED */
  129. X    }
  130. X}
  131. X
  132. XSPtoken *
  133. XSPqexec(tk)
  134. XSPtoken *tk;
  135. X{
  136. X    if (!tk) return NIL;
  137. X    if (tk->type==SPTokList) return SPexec(tk);
  138. X    return tk;
  139. X}
  140. X
  141. Xint
  142. XSPbool(tk)    /* returns boolean value for token */
  143. XSPtoken *tk;
  144. X{
  145. X    if (!tk) return 0;
  146. X    switch (tk->type) {
  147. X    case SPTokInt:
  148. X        return (tk->value.n!=0);
  149. X    case SPTokFloat:
  150. X        return (tk->value.f!=0.0);
  151. X    case SPTokNil:
  152. X        return 0;
  153. X    case SPTokStr:
  154. X    case SPTokName:
  155. X        return (tk->value.s!=0 && tk->value.s[0]!=0);
  156. X    case SPTokList:
  157. X        return (tk->value.l!=0);
  158. X    default:
  159. X        SPescape("UnknownType","unknown node type %c",tk->type);
  160. X        /* NOTREACHED */
  161. X    }
  162. X}
  163. X
  164. Xint
  165. XSPbooleval(tk)
  166. XSPtoken *tk;
  167. X{
  168. X    return SPbool(SPqexec(tk));
  169. X}
  170. X
  171. XSPtoken *
  172. XSPexecname(tk)
  173. XSPtoken *tk;
  174. X{
  175. Xchar *name;
  176. XSPfuncinfo *finfo, *SPfindfunc();
  177. XSPtoken *tkval;
  178. Xint argc;
  179. Xint argv[100];
  180. Xchar *argstr;
  181. Xint argtype;
  182. XSPtoken *rval;
  183. Xint rtype;
  184. Xint t;
  185. Xint n;
  186. Xfloat f;
  187. Xchar *s;
  188. XSPtoken *l;
  189. Xint dflti;
  190. Xchar *dflts, *dflts0;
  191. Xdouble *dptr;
  192. Xint (*ifp)();
  193. Xdouble (*ffp)();
  194. Xchar * (*sfp)();
  195. XSPtoken * (*lfp)();
  196. Xstatic char *badargs="BadArgument";
  197. Xstatic char *toomanyargsdef="TooManyArgsDef";
  198. Xstatic char *badargstr="BadArgstrFormat";
  199. X
  200. X    if (!tk || tk->type!=SPTokName) return NIL;
  201. X    name = tk->value.s;
  202. X#if 0
  203. Xprintf("execname %s\n", name);
  204. X#endif
  205. X    finfo = SPfindfunc(name);
  206. X    if (!finfo) {
  207. X        /* maybe it's a user-defined function */
  208. X        if (SPxexecp) {
  209. X            ALLOCTOKEN(rval)
  210. X            t = (*SPxexecp)(name,tk->next,rval);
  211. X            if (t) return rval;    /* he did it! */
  212. X            FREETOKEN(rval)
  213. X        }
  214. X        SPescape("NoSuchFunction","can't fund function %s",name);
  215. X        /* NOTREACHED */
  216. X    }
  217. X    argc = 0;
  218. X    argstr = finfo->args+1;
  219. X    tk = tk->next;
  220. X    while (*argstr && *argstr!=';') {
  221. X        argtype = *argstr;
  222. X        switch (argtype) {
  223. X        case 'b':        /* any type, converted to bool int */
  224. X            tkval = SPqexec(tk);
  225. X            if (!tkval) {
  226. X                SPescape(badargs,"needed arg for %s",name);
  227. X                /* NOTREACHED */
  228. X            }
  229. X            argv[argc++] = SPbool(tkval);
  230. X            break;
  231. X        case 'i':        /* int */
  232. X            tkval = SPqexec(tk);
  233. X            if (tkval && tkval->type==SPTokInt) {
  234. X                argv[argc++] = tkval->value.n;
  235. X            }
  236. X            else {
  237. X                SPescape(badargs,"needed int for %s",name);
  238. X                /* NOTREACHED */
  239. X            }
  240. X            break;
  241. X        case 'I':    /* optional int */
  242. X            if (argstr[1]=='-') {
  243. X                argstr++;
  244. X                dflti = -atoi(argstr+1);
  245. X            } else {
  246. X                dflti = atoi(argstr+1);
  247. X            }
  248. X            while (isdigit(argstr[1])) argstr++;
  249. X            tkval = SPqexec(tk);
  250. X            if (tkval)
  251. X                if (tkval->type==SPTokInt) {
  252. X                    argv[argc++] = tkval->value.n;
  253. X                }
  254. X                else {
  255. X                    SPescape(badargs,"needed int for %s",
  256. X                        name);
  257. X                    /* NOTREACHED */
  258. X                }
  259. X            else {
  260. X                argv[argc++] = dflti;
  261. X            }
  262. X            break;
  263. X        case 'f':        /* float */
  264. X            tkval = SPqexec(tk);
  265. X            if (tkval && tkval->type==SPTokFloat) {
  266. X                dptr = (double *)(argv+argc);
  267. X                *dptr = (double)(tkval->value.f);
  268. X                argc = ((int *)dptr)-argv;
  269. X            }
  270. X            else {
  271. X                SPescape(badargs,"needed float for %s",name);
  272. X                /* NOTREACHED */
  273. X            }
  274. X            break;
  275. X        case 'n':        /* name */
  276. X        case 's':        /* string */
  277. X            tkval = SPqexec(tk);
  278. X            if (tkval && (tkval->type==SPTokName ||
  279. X                (argtype=='s'&&tkval->type==SPTokStr))) {
  280. X                ((char **)argv)[argc++] = tkval->value.s;
  281. X            }
  282. X            else {
  283. X                SPescape(badargs,"needed %s for %s",
  284. X                    argtype=='n'?"name":"string",name);
  285. X                /* NOTREACHED */
  286. X            }
  287. X            break;
  288. X        case 'S':        /* optional string */
  289. X            if (argstr[1]=='N') {
  290. X                dflts = NIL;
  291. X                ++argstr;
  292. X            }
  293. X            else if (argstr[1]=='"') {    /* read str */
  294. X                argstr += 2;    /* point past quote */
  295. X                dflts0 = argstr;
  296. X                while (*argstr!=0 && *argstr!='"') {
  297. X                    argstr++;
  298. X                }
  299. X                dflts = XALLOC(char,argstr-dflts0+1);
  300. X                strncpy(dflts,dflts0,argstr-dflts0);
  301. X                dflts[argstr-dflts0]=0;
  302. X            }
  303. X            else {
  304. X                SPescape("BadArgstrFormat",
  305. X                    "bad format in arg string for %s",
  306. X                    name);
  307. X                /* NOTREACHED */
  308. X            }
  309. X            tkval = SPqexec(tk);
  310. X            if (tkval) {
  311. X                if ((tkval->type==SPTokName ||
  312. X                    (argtype=='S'&&tkval->type==SPTokStr))) {
  313. X                    ((char **)argv)[argc++] =
  314. X                        tkval->value.s;
  315. X                    XFREE(dflts);
  316. X                }
  317. X                else {
  318. X                    SPescape(badargs,"needed %s for %s",
  319. X                        argtype=='n'?"name":"string",name);
  320. X                    /* NOTREACHED */
  321. X                }
  322. X            }
  323. X            else {
  324. X                ((char **)argv)[argc++] = dflts;
  325. X            }
  326. X            break;
  327. X        case 'V':        /* single evaluated variable */
  328. X            tkval = SPqexec(tk);
  329. X            if (!tkval) tkval=SPnewnil();
  330. X            ((SPtoken **)argv)[argc++] = tkval;
  331. X            break;
  332. X        case 'L':        /* unevaluated list */
  333. X            ((SPtoken **)argv)[argc++] = tk;
  334. X            break;
  335. X        case 'R':    /* remainder of list as one arg, uneval. */
  336. X            ALLOCTOKEN(tkval)
  337. X            tkval->type = SPTokList;
  338. X            tkval->next = 0;
  339. X            tkval->value.l = tk;
  340. X            tk = 0;
  341. X            ((SPtoken **)argv)[argc++] = tkval;
  342. X            break;
  343. X        default:
  344. X            SPescape(badargstr,
  345. X                "bad arg type %c in func %s",argtype,name);
  346. X            /* NOTREACHED */
  347. X        }
  348. X        if (*argstr) argstr++;
  349. X        if (tk) tk = tk->next;
  350. X    }
  351. X    if (tk) {
  352. X        SPescape("TooManyArgs","too many arguments for %s",name);
  353. X        /* NOTREACHED */
  354. X    }
  355. X    if (*argstr && *argstr!=';') {
  356. X        SPescape("NotEnoughArgs","not enough arguments for %s", name);
  357. X        /* NOTREACHED */
  358. X    }
  359. X    ALLOCTOKEN(rval)
  360. X    rtype = finfo->args[0];
  361. X    switch (rtype) {    /* return value type */
  362. X    case 'i':    /* int */
  363. X    case 'v':    /* no return value */
  364. X        ifp = finfo->funcp;
  365. X        switch (argc) {
  366. X        case 0: n = (*ifp)(); break;
  367. X        case 1: n = (*ifp)(argv[0]); break;
  368. X        case 2: n = (*ifp)(argv[0],argv[1]); break;
  369. X        case 3: n = (*ifp)(argv[0],argv[1],argv[2]); break;
  370. X        case 4: n = (*ifp)(argv[0],argv[1],argv[2],argv[3]); break;
  371. X        default:
  372. X            SPescape(toomanyargsdef,
  373. X                "too many args in definition of %s",name);
  374. X            /* NOTREACHED */
  375. X        }
  376. X        if (rtype=='v') {
  377. X            rval->type = SPTokNil;
  378. X        } else {
  379. X            rval->type = SPTokInt;
  380. X            rval->value.n = n;
  381. X        }
  382. X        break;
  383. X    case 'f':    /* float (double) */
  384. X        ffp = (dblfuncp)(finfo->funcp);
  385. X        switch (argc) {
  386. X        case 0: f = (*ffp)(); break;
  387. X        case 1: f = (*ffp)(argv[0]); break;
  388. X        case 2: f = (*ffp)(argv[0],argv[1]); break;
  389. X        case 3: f = (*ffp)(argv[0],argv[1],argv[2]); break;
  390. X        case 4: f = (*ffp)(argv[0],argv[1],argv[2],argv[3]); break;
  391. X        default:
  392. X            SPescape(toomanyargsdef,
  393. X                "too many args in definition of %s",name);
  394. X            /* NOTREACHED */
  395. X        }
  396. X        rval->type = SPTokFloat;
  397. X        rval->value.f = f;
  398. X        break;
  399. X    case 'n':    /* name */
  400. X    case 's':    /* string */
  401. X    case 'S':    /* allocated string */
  402. X        sfp = (strfuncp)(finfo->funcp);
  403. X        switch (argc) {
  404. X        case 0: s = (*sfp)(); break;
  405. X        case 1: s = (*sfp)(argv[0]); break;
  406. X        case 2: s = (*sfp)(argv[0],argv[1]); break;
  407. X        case 3: s = (*sfp)(argv[0],argv[1],argv[2]); break;
  408. X        case 4: s = (*sfp)(argv[0],argv[1],argv[2],argv[3]); break;
  409. X        default:
  410. X            SPescape(toomanyargsdef,
  411. X                "too many args in definition of %s",name);
  412. X            /* NOTREACHED */
  413. X        }
  414. X        if (rtype=='n')
  415. X            rval->type = SPTokName;
  416. X        else
  417. X            rval->type = SPTokStr;
  418. X        if (islower(rtype) || !s) {
  419. X            if (!s) s="";
  420. X            rval->value.s =
  421. X                XALLOCM(char,strlen(s)+1,"eval str func");
  422. X        }
  423. X        else {
  424. X            rval->value.s = s;    /* allocated for us */
  425. X        }
  426. X        strcpy(rval->value.s,s);
  427. X        break;
  428. X    case 'V':    /* returns an already allocated var token */
  429. X    case 'l':    /* returns a static list */
  430. X    case 'L':    /* returns an already-allocated list */
  431. X        lfp = (listfuncp)finfo->funcp;
  432. X        switch (argc) {
  433. X        case 0: l = (*lfp)(); break;
  434. X        case 1: l = (*lfp)(argv[0]); break;
  435. X        case 2: l = (*lfp)(argv[0],argv[1]); break;
  436. X        case 3: l = (*lfp)(argv[0],argv[1],argv[2]); break;
  437. X        case 4: l = (*lfp)(argv[0],argv[1],argv[2],argv[3]); break;
  438. X        default:
  439. X            SPescape(toomanyargsdef,
  440. X                "too many args in definition of %s",name);
  441. X            /* NOTREACHED */
  442. X        }
  443. X        FREETOKEN(rval)
  444. X        if (islower(rtype))
  445. X            rval = SPcopytoken(l);
  446. X        else
  447. X            rval = l;
  448. X        break;
  449. X    default:
  450. X        SPescape(badargstr,"bad return code type %c for %s",rtype,name);
  451. X        rval->type = SPTokNil;
  452. X        break;
  453. X    }
  454. X    return rval;
  455. X}
  456. X
  457. X/* execute all of the nodes in a list of nodes */
  458. XSPtoken *
  459. XSPexeclist(tklist)
  460. XSPtoken *tklist;
  461. X{
  462. XSPtoken *rval;
  463. Xjmp_bufp savejbufp;
  464. Xjmp_buf ourjbuf;
  465. XSPtoken *tk, *jtk;
  466. X
  467. X    rval = NIL;
  468. X    savejbufp = SPjbufp;
  469. X    SPjbufp = jmpbuf_addr(ourjbuf);
  470. X    for (tk=tklist; tk; tk=tk->next) {
  471. X        if (rval) FREETOKEN(rval)
  472. X        if (setjmp(jmpbuf_ref(SPjbufp))) {    /* process goto */
  473. X            for (jtk=tklist; jtk; jtk=jtk->next) {
  474. X                if (SPisgotolabel(jtk)) {
  475. X                    tk = jtk;    /* go there */
  476. X                    goto foundlabel; /* resume execution */
  477. X                }
  478. X            }
  479. X            /* didn't find the label, keep going up */
  480. X            SPjbufp = savejbufp;
  481. X            longjmp(jmpbuf_ref(SPjbufp),1);
  482. X        }
  483. Xfoundlabel:
  484. X        rval = SPexec(tk);    /* execute one node */
  485. X    }
  486. X    SPjbufp = savejbufp;
  487. X    return rval;
  488. X}
  489. X
  490. Xint    /* returns 1 if the node is a label list and matches SPgotolabel */
  491. XSPisgotolabel(tk)
  492. XSPtoken *tk;
  493. X{
  494. XSPtoken *tkl, *tkln;
  495. X
  496. X    if (tk &&
  497. X        tk->type==SPTokList &&
  498. X        ((tkl=tk->value.l)) &&
  499. X        tkl->type==SPTokName &&
  500. X        tkl->value.s &&
  501. X        strcmp(tkl->value.s,"label")==0 &&
  502. X        ((tkln=tkl->next)) &&
  503. X        tkln->type==SPTokName &&
  504. X        tkln->value.s &&
  505. X        strcmp(tkln->value.s,SPgotolabel)==0
  506. X       ) {
  507. X        return 1;    /* found it */
  508. X    }
  509. X    return 0;    /* not this one */
  510. X}
  511. X
  512. X/*..........*/
  513. X
  514. XSPfuncinfo *SPfuncbase;
  515. X
  516. XSPfuncinfo *
  517. XSPfindfunc(name)
  518. Xchar *name;        /* name of the func to find */
  519. X{
  520. XSPfuncinfo *finfo;
  521. X
  522. X    for (finfo=SPfuncbase; finfo; finfo=finfo->next)
  523. X        if (strcmp(finfo->name,name)==0) return finfo;
  524. X    return NIL;
  525. X}
  526. X
  527. XSPfuncinfo *
  528. XSPnewfunc(name)        /* make a new entry for the name */
  529. Xchar *name;
  530. X{
  531. XSPfuncinfo *finfo;
  532. X
  533. X    finfo = XALLOCM(SPfuncinfo,1,"newfunc");
  534. X    finfo->name = name;
  535. X    finfo->next = SPfuncbase;
  536. X    SPfuncbase = finfo;
  537. X    return finfo;
  538. X}
  539. X
  540. X/* VARARGS2 */ /* not really - but third arg is of variable type */
  541. Xvoid
  542. XSPdeffunc(name,args,funcp)
  543. Xchar *name;        /* the name of the function */
  544. Xchar *args;        /* type of args encoded as string */
  545. Xvoid (*funcp)();    /* pointer to the function */
  546. X{
  547. XSPfuncinfo *finfo;
  548. X
  549. X    if (strlen(args)<1) {
  550. X        SPwerror("args string for %s is too short", name);
  551. X        return;
  552. X    }
  553. X    finfo = SPfindfunc(name);    /* find the function */
  554. X    if (finfo) {        /* redefinition */
  555. X        SPwerror("%s redefined",name);
  556. X    }
  557. X    else {            /* new */
  558. X        finfo = SPnewfunc(name);
  559. X    }
  560. X    finfo->args = args;
  561. X    finfo->funcp = funcp;
  562. X}
  563. X
  564. X/*..........*/
  565. X
  566. XSPprintval(stream,tk,indent)
  567. XFILE *stream;
  568. XSPtoken *tk;
  569. Xint indent;
  570. X{
  571. Xint i;
  572. XSPtoken *ltk;
  573. X
  574. X    for (i=0;i<indent;i++)
  575. X        fputs("  ",stream);
  576. X    if (!tk) fprintf(stream,"<NIL>\n");
  577. X    else switch (tk->type) {
  578. X    case SPTokNil: fprintf(stream,"NIL\n"); break;
  579. X    case SPTokInt: fprintf(stream,"INT %d\n",tk->value.n); break;
  580. X    case SPTokFloat: fprintf(stream,"FLOAT %g\n",tk->value.f); break;
  581. X    case SPTokStr: fprintf(stream,"STRING %s\n",tk->value.s); break;
  582. X    case SPTokName: fprintf(stream,"NAME %s\n",tk->value.s); break;
  583. X    case SPTokList:
  584. X        fprintf(stream,"LIST:\n");
  585. X        for (ltk=tk->value.l; ltk; ltk=ltk->next)
  586. X            SPprintval(stream,ltk,indent+1);
  587. X        break;
  588. X    default:
  589. X        fprintf(stream,"Type %03o (%c)\n",tk->type,tk->type);
  590. X        break;
  591. X    }
  592. X}
  593. X
  594. X/*..........*/
  595. X
  596. X/* some debug routines which print out tokens */
  597. Xvoid
  598. XSPdumptoken(tk)
  599. XSPtoken *tk;
  600. X{
  601. X    if (!tk) {
  602. X        printf("NIL pointer\n");
  603. X        return;
  604. X    }
  605. X    if (!isprint(tk->type)) {
  606. X        printf("bad type: %03o\n", tk->type);
  607. X        return;
  608. X    }
  609. X    printf("type=%c",tk->type);
  610. X    switch (tk->type) {
  611. X    case SPTokInt:
  612. X        printf(" %d", tk->value.n);
  613. X        break;
  614. X    case SPTokFloat:
  615. X        printf(" %f", tk->value.f);
  616. X        break;
  617. X    case SPTokStr:
  618. X    case SPTokName:
  619. X        printf(" %s", tk->value.s);
  620. X        break;
  621. X    case SPTokList:
  622. X        printf("\n");
  623. X        SPdumptokenlist(tk->value.l);
  624. X        break;
  625. X    }
  626. X    printf("\n");
  627. X}
  628. X
  629. Xvoid
  630. XSPdumptokenlist(tk)
  631. XSPtoken *tk;
  632. X{
  633. X    while (tk) {
  634. X        SPdumptoken(tk);
  635. X        tk = tk->next;
  636. X    }
  637. X}
  638. X
  639. X/* end */
  640. END_OF_FILE
  641. if test 13333 -ne `wc -c <'exec.c'`; then
  642.     echo shar: \"'exec.c'\" unpacked with wrong size!
  643. fi
  644. # end of 'exec.c'
  645. fi
  646. echo shar: End of archive 2 \(of 2\).
  647. cp /dev/null ark2isdone
  648. MISSING=""
  649. for I in 1 2 ; do
  650.     if test ! -f ark${I}isdone ; then
  651.     MISSING="${MISSING} ${I}"
  652.     fi
  653. done
  654. if test "${MISSING}" = "" ; then
  655.     echo You have unpacked both archives.
  656.     rm -f ark[1-9]isdone
  657. else
  658.     echo You still need to unpack the following archives:
  659.     echo "        " ${MISSING}
  660. fi
  661. ##  End of shell archive.
  662. exit 0
  663.